﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Ue.Infrastructure.Caching;

namespace Ue.Tests.Infrastructure.Caching
{
    [TestClass]
    public class CacheTests
    {
        private Mock<ICacheContextAccessor> mockCacheContextAccessor;
        private ICache<string, object> cache;

        [TestInitialize]
        public void Init()
        {
            this.mockCacheContextAccessor = new Mock<ICacheContextAccessor>();
            this.cache = new Cache<string, object>(mockCacheContextAccessor.Object);
        }

        [TestMethod]
        public void ResultByCache()
        {
            Func<object> acquireObject = () => cache.Get("key1", context =>
            {
                return new Object();
            });

            Assert.AreEqual(acquireObject(), acquireObject());
        }

        [TestMethod]
        public void TokenExpired()
        {
            var counter=0;
            Mock<IVolatileToken> mockToken = new Mock<IVolatileToken>();
            mockToken.Setup(item => item.IsCurrent)
                .Callback(() => counter++)
                .Returns(()=>counter < 2);

            Func<object> acquireObject = () => cache.Get("key1", context =>
            {
                context.Monitor(mockToken.Object);
                return new Object();
            });

            var obj=acquireObject();
            Assert.AreEqual(obj, acquireObject());
            Assert.AreNotEqual(obj, acquireObject());
        }

        [TestMethod]
        public void RelatingToken()
        {
            IAcquireContext acquireContext = null;
            mockCacheContextAccessor.Setup(item => item.Current)
                .Returns(()=>acquireContext);

            mockCacheContextAccessor.SetupSet<IAcquireContext>(item => item.Current = It.IsAny<IAcquireContext>())
                .Callback(item => acquireContext = item);

            var counter = 0;
            var subCounter = 0;

            Mock<IVolatileToken> mockToken = new Mock<IVolatileToken>();
            Mock<IVolatileToken> mockSubToken = new Mock<IVolatileToken>();

            mockToken.Setup(item => item.IsCurrent)
                .Returns(() => counter < 2);
            mockSubToken.Setup(item => item.IsCurrent)
                .Returns(() => subCounter < 2);

            Func<string,object> acquireObject = key => cache.Get(key, context =>
            {
                var subResult = this.cache.Get("sub" + key, subContext =>
                {
                    subContext.Monitor(mockSubToken.Object);

                    return new Object();
                });

                context.Monitor(mockToken.Object);

                return ((new object().GetHashCode() << 5) + new object().GetHashCode()) ^ subResult.GetHashCode();
            });

            var obj = acquireObject("key1");
            Assert.AreEqual(obj, acquireObject("key1"));
            counter = 2;
            Assert.AreNotEqual(obj, acquireObject("key1"));

            counter = 0;
            subCounter = 0;
            var obj2 = acquireObject("key2");
            Assert.AreEqual(obj2, acquireObject("key2"));
            subCounter = 2;
            Assert.AreNotEqual(obj2, acquireObject("key2"));
        }
    }
}
